"use client"; import { useState, useEffect, useCallback, useMemo } from "react"; import { useParams } from "next/navigation"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Skeleton } from "@/components/ui/skeleton"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { InfoIcon, RefreshCw, Search, Upload, Plus, Loader2 } from "lucide-react"; import { toast } from "sonner"; import { useTranslation } from "@/i18n/client"; import { UnifiedDwgReceiptItem, DetailDwgReceiptItem, FileInfoItem, fetchDwgReceiptList, getVendorSessionInfo, fetchVendorProjects, fetchDetailDwgReceiptList, fetchFileInfoList, } from "@/lib/dolce/actions"; import { DrawingListTableV2 } from "@/lib/dolce/table/drawing-list-table-v2"; import { drawingListColumns } from "@/lib/dolce/table/drawing-list-columns"; import { createGttDrawingListColumns, DocumentType } from "@/lib/dolce/table/gtt-drawing-list-columns"; import { createDetailDrawingColumns } from "@/lib/dolce/table/detail-drawing-columns"; import { createFileListColumns } from "@/lib/dolce/table/file-list-columns"; // V3: Sync 기능 없이 일괄 업로드 (MatchBatchFileDwg / Edit 사용) import { B4BulkUploadDialogV3 } from "@/lib/dolce/dialogs/b4-bulk-upload-dialog-v3"; // V1로 되돌리려면: 위 줄을 주석 처리하고 아래 줄의 주석을 해제하세요 // import { B4BulkUploadDialog } from "@/lib/dolce/dialogs/b4-bulk-upload-dialog"; import { AddAndModifyDetailDrawingDialog } from "@/lib/dolce/dialogs/add-and-modify-detail-drawing-dialog"; import { UploadFilesToDetailDialog } from "@/lib/dolce/dialogs/upload-files-to-detail-dialog"; interface DolceUploadPageV2Props { searchParams: { [key: string]: string | string[] | undefined }; } export default function DolceUploadPageV2({ searchParams }: DolceUploadPageV2Props) { const params = useParams(); const lng = params?.lng as string; const { t } = useTranslation(lng, "dolce"); // URL에서 초기 프로젝트 코드 const initialProjNo = (searchParams.projNo as string) || ""; // 상태 관리 const [drawings, setDrawings] = useState([]); const [projects, setProjects] = useState>([]); const [vendorInfo, setVendorInfo] = useState<{ userId: string; userName: string; email: string; vendorCode: string; vendorName: string; drawingKind: "B3" | "B4"; } | null>(null); const [isLoading, setIsLoading] = useState(true); const [isRefreshing, setIsRefreshing] = useState(false); const [error, setError] = useState(null); // 필터 상태 const [projNo, setProjNo] = useState(initialProjNo); const [drawingNo, setDrawingNo] = useState(""); const [drawingName, setDrawingName] = useState(""); const [discipline, setDiscipline] = useState(""); const [manager, setManager] = useState(""); const [documentType, setDocumentType] = useState("ALL"); // B4 전용 // 선택된 도면 및 상세도면 const [selectedDrawing, setSelectedDrawing] = useState(null); const [detailDrawings, setDetailDrawings] = useState([]); const [selectedDetail, setSelectedDetail] = useState(null); const [files, setFiles] = useState([]); const [isLoadingDetails, setIsLoadingDetails] = useState(false); const [isLoadingFiles, setIsLoadingFiles] = useState(false); // 다이얼로그 const [bulkUploadDialogOpen, setBulkUploadDialogOpen] = useState(false); const [addDialogOpen, setAddDialogOpen] = useState(false); const [dialogMode, setDialogMode] = useState<"add" | "edit">("add"); const [editingDetail, setEditingDetail] = useState(null); const [uploadFilesDialogOpen, setUploadFilesDialogOpen] = useState(false); // 초기 데이터 로드 const loadInitialData = useCallback(async () => { try { setIsLoading(true); setError(null); // 병렬로 데이터 로드 const [vendorInfoData, projectsData] = await Promise.all([ getVendorSessionInfo(), fetchVendorProjects(), ]); setVendorInfo(vendorInfoData as typeof vendorInfo); setProjects(projectsData); // 초기 프로젝트가 있으면 도면 로드 if (initialProjNo) { const drawingsData = await fetchDwgReceiptList({ project: initialProjNo, drawingKind: vendorInfoData.drawingKind, drawingVendor: vendorInfoData.drawingKind === "B3" ? vendorInfoData.vendorCode : "", }); setDrawings(drawingsData); } } catch (err) { console.error("초기 데이터 로드 실패:", err); setError(err instanceof Error ? err.message : t("page.initialLoadError")); toast.error(t("page.initialLoadError")); } finally { setIsLoading(false); } }, [initialProjNo, t]); // 도면 목록 조회 const loadDrawings = useCallback(async () => { if (!projNo || !vendorInfo) return; try { setIsRefreshing(true); setError(null); const drawingsData = await fetchDwgReceiptList({ project: projNo, drawingKind: vendorInfo.drawingKind, drawingVendor: vendorInfo.drawingKind === "B3" ? vendorInfo.vendorCode : "", }); setDrawings(drawingsData); toast.success(t("page.drawingLoadSuccess")); } catch (err) { console.error("도면 로드 실패:", err); setError(err instanceof Error ? err.message : t("page.drawingLoadError")); toast.error(t("page.drawingLoadError")); } finally { setIsRefreshing(false); } }, [projNo, vendorInfo, t]); // 상세도면 목록 로드 const loadDetailDrawings = useCallback(async () => { if (!selectedDrawing) { setDetailDrawings([]); setSelectedDetail(null); return; } try { setIsLoadingDetails(true); const data = await fetchDetailDwgReceiptList({ project: selectedDrawing.ProjectNo, drawingNo: selectedDrawing.DrawingNo, discipline: selectedDrawing.Discipline, drawingKind: selectedDrawing.DrawingKind, userId: "", // 조회 시 모든 사용자의 상세도면을 보기 위해 빈 문자열 전달 }); setDetailDrawings(data); // 첫 번째 상세도면 자동 선택 if (data.length > 0) { setSelectedDetail(data[0]); } else { setSelectedDetail(null); } } catch (error) { console.error("상세도면 로드 실패:", error); toast.error(t("detailDialog.detailLoadError")); setDetailDrawings([]); setSelectedDetail(null); } finally { setIsLoadingDetails(false); } }, [selectedDrawing, t]); // 파일 목록 로드 const loadFiles = useCallback(async () => { if (!selectedDetail) { setFiles([]); return; } try { setIsLoadingFiles(true); const data = await fetchFileInfoList(selectedDetail.UploadId); setFiles(data); } catch (error) { console.error("파일 목록 로드 실패:", error); toast.error(t("detailDialog.fileLoadError")); setFiles([]); } finally { setIsLoadingFiles(false); } }, [selectedDetail, t]); // 초기 데이터 로드 useEffect(() => { loadInitialData(); }, [loadInitialData]); // 프로젝트 변경 시 자동 검색 useEffect(() => { if (projNo && vendorInfo) { loadDrawings(); } }, [projNo, vendorInfo, loadDrawings]); // 선택된 도면 변경 시 상세도면 로드 useEffect(() => { loadDetailDrawings(); }, [selectedDrawing, loadDetailDrawings]); // 선택된 상세도면 변경 시 파일 목록 로드 useEffect(() => { loadFiles(); }, [selectedDetail, loadFiles]); // 도면 클릭 핸들러 const handleDrawingClick = (drawing: UnifiedDwgReceiptItem) => { setSelectedDrawing(drawing); }; // 검색 핸들러 const handleSearch = () => { loadDrawings(); }; // 새로고침 핸들러 const handleRefresh = () => { loadDrawings(); }; // 상세도면 새로고침 핸들러 const handleRefreshDetails = () => { loadDetailDrawings(); }; // 일괄 업로드 완료 핸들러 const handleBulkUploadComplete = () => { loadDrawings(); }; // 상세도면 추가 완료 핸들러 const handleAddComplete = () => { setAddDialogOpen(false); loadDetailDrawings(); }; // 파일 업로드 완료 핸들러 const handleUploadComplete = () => { setUploadFilesDialogOpen(false); loadFiles(); }; // 파일 다운로드 핸들러 const handleDownload = async (file: FileInfoItem) => { try { toast.info(t("detailDialog.downloadPreparing")); // 파일 생성자의 userId를 사용하여 다운로드 const response = await fetch("/api/dolce/download", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ fileId: file.FileId, userId: file.CreateUserId, // 파일 생성자의 ID 사용 fileName: file.FileName, }), }); if (!response.ok) { throw new Error(t("detailDialog.downloadError")); } const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = file.FileName; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); toast.success(t("detailDialog.downloadSuccess")); } catch (error) { console.error("파일 다운로드 실패:", error); toast.error(t("detailDialog.downloadError")); } }; // 상세도면 수정 핸들러 const handleEditDetail = (detail: DetailDwgReceiptItem) => { setDialogMode("edit"); setEditingDetail(detail); setAddDialogOpen(true); }; // 필터된 도면 목록 (클라이언트 사이드 필터링) const filteredDrawings = useMemo(() => { let result = drawings.filter((drawing) => { // 도면번호 필터 (공백 포함) if (drawingNo && !drawing.DrawingNo.toLowerCase().includes(drawingNo.toLowerCase())) { return false; } // 도면명 필터 (공백 포함) if (drawingName && !drawing.DrawingName.toLowerCase().includes(drawingName.toLowerCase())) { return false; } // 설계공종 필터 (공백 포함) if (discipline && !drawing.Discipline?.toLowerCase().includes(discipline.toLowerCase())) { return false; } // 담당자명 필터 (공백 포함) if (manager && !drawing.Manager.toLowerCase().includes(manager.toLowerCase()) && !drawing.ManagerENM?.toLowerCase().includes(manager.toLowerCase())) { return false; } return true; }); // B4인 경우 Document Type 필터 적용 if (vendorInfo?.drawingKind === "B4" && documentType !== "ALL") { result = result.filter((drawing) => { // B4 타입 체크 if (drawing.DrawingKind !== "B4") return false; // B4 도면의 DrawingMoveGbn 체크 const gttDrawing = drawing as { DrawingMoveGbn?: string }; if (documentType === "SHI_INPUT") { return gttDrawing.DrawingMoveGbn === "도면제출"; } else if (documentType === "GTT_DELIVERABLES") { return gttDrawing.DrawingMoveGbn === "도면입수"; } return true; }); } return result; }, [drawings, drawingNo, drawingName, discipline, manager, vendorInfo?.drawingKind, documentType]); // RegisterId + UploadId 조합으로 고유 ID 생성 const getDetailDrawingId = (detail: DetailDwgReceiptItem) => { return `${detail.RegisterId}_${detail.UploadId}`; }; // 도면 고유 ID 생성 const getDrawingId = (drawing: UnifiedDwgReceiptItem) => { return `${drawing.ProjectNo}_${drawing.DrawingNo}_${drawing.Discipline}`; }; // B4인 경우 "도면입수"인 건만 상세도면 추가 및 파일 첨부 가능 // B3인 경우 모든 건에 대해 가능 const canAddDetailDrawing = vendorInfo && ( vendorInfo.drawingKind === "B3" || (vendorInfo.drawingKind === "B4" && selectedDrawing && 'DrawingMoveGbn' in selectedDrawing && selectedDrawing.DrawingMoveGbn === "도면입수") ); const fileColumns = createFileListColumns({ onDownload: handleDownload, lng }); if (isLoading) { return ( ); } return (
{/* 에러 메시지 */} {error && ( {error} )} {/* 안내 메시지 */} {!projNo && ( {t("page.selectProject")} )} {/* 필터 카드 - 슬림하게 */} {t("filter.title")}
{/* 프로젝트 선택 */}
{/* 도면번호 검색 */}
setDrawingNo(e.target.value)} placeholder={t("filter.drawingNoPlaceholder")} />
{/* 도면명 검색 */}
setDrawingName(e.target.value)} placeholder={t("filter.drawingNamePlaceholder")} />
{/* 설계공종 검색 */}
setDiscipline(e.target.value)} placeholder={t("filter.disciplinePlaceholder")} />
{/* 담당자명 검색 */}
setManager(e.target.value)} placeholder={t("filter.managerPlaceholder")} />
{/* B4(GTT) 전용: Document Type 필터 */} {vendorInfo?.drawingKind === "B4" && (
)}
{/* B4 벤더인 경우에만 일괄 업로드 버튼 표시 */} {vendorInfo?.drawingKind === "B4" && ( )}
{/* 도면 리스트 테이블 - 항상 렌더링 */} {t("drawingList.title")} {filteredDrawings.length > 0 && ` ${t("drawingList.count", { count: filteredDrawings.length })}`} {!projNo || !vendorInfo ? (

{t("page.selectProject")}

) : isRefreshing ? (
) : ( )}
{/* 하단: 상세도면리스트 + 파일리스트 - 항상 렌더링 */}
{/* 좌측: 상세도면 리스트 */} {t("detailDialog.detailListTitle")} {selectedDrawing && ( {selectedDrawing.DrawingNo} )}
{canAddDetailDrawing && ( )}
{!selectedDrawing ? (

Select a drawing

) : isLoadingDetails ? (
) : ( columns={createDetailDrawingColumns(lng, t, handleEditDetail)} data={detailDrawings} onRowClick={setSelectedDetail} selectedRow={selectedDetail || undefined} getRowId={getDetailDrawingId} minHeight="0" defaultPageSize={"all"} /> )}
{/* 우측: 첨부파일 리스트 */} {t("detailDialog.fileListTitle")} {selectedDetail && ( Rev. {selectedDetail.DrawingRevNo} )} {selectedDetail && canAddDetailDrawing && ( )} {!selectedDetail ? (

{t("detailDialog.selectDetailDrawing")}

) : isLoadingFiles ? (
) : ( )}
{/* B4 일괄 업로드 다이얼로그 (V3) */} {/* V3: Sync 기능 없이 일괄 업로드 (MatchBatchFileDwg / Edit 사용) */} {vendorInfo && vendorInfo.drawingKind === "B4" && projNo && ( )} {/* V1로 되돌리려면: 위의 B4BulkUploadDialogV3를 B4BulkUploadDialog로 변경하세요 */} {/* 상세도면 추가 다이얼로그 */} {vendorInfo && selectedDrawing && ( )} {/* 파일 업로드 다이얼로그 */} {vendorInfo && selectedDetail && ( )}
); }